package com.optitronics.beans;

import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;

import com.optitronics.service.ReportService;
import com.optitronics.util.OptitronicsConstants;
import com.optitronics.util.OptitronicsUtils;

/**
 * Session scopen bean, defined in applicationContext.xml. <aop:scoped-proxy/>
 * is used so this bean can be injected in singletone beans
 * 
 * @author Bojan Vukotic
 * 
 */
public class ShoppingCart implements Serializable {

	private static final long serialVersionUID = 1L;

	private Log logger = LogFactory.getLog(this.getClass());

	private Long cartId;

	private BigDecimal totalAmoutWithoutTax;

	private BigDecimal totalAmoutWithTax;

	private List<ShoppingCartItem> items;

	@Autowired
	private ReportService reportService;

	public ShoppingCart() {

		this.items = new ArrayList<ShoppingCartItem>();
		this.cartId = System.currentTimeMillis();
	}

	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
	public ResellerOrder saveShoppingCart() throws Exception {
		logger.info(OptitronicsUtils.getLoggerPrefix() + ": " + "Saving shopping cart");
		Reseller activeReseller = Reseller.findResellerByEmail(OptitronicsUtils.getActiveUser());
		int ordinal = 0;

		ResellerOrder order = new ResellerOrder();
		order.setOrderDate(new Date());
		order.setReseller(activeReseller);
		String orderNumber = ResellerOrder.generateOrderNumber();
		order.setOrderNumber(orderNumber);
		logger.info(OptitronicsUtils.getLoggerPrefix() + ": " + "Generated order number: " + orderNumber);

		order.persist();

		for (ShoppingCartItem cartItem : items) {
			ordinal++;
			OrderItem orderItem = new OrderItem();
			orderItem.setOrdinal(ordinal);
			orderItem.setQuantity(cartItem.getQuantity());
			orderItem.setBuyerAddress(cartItem.getBuyerAddress());
			orderItem.setBuyerCity(cartItem.getBuyerCity());
			orderItem.setBuyerContactPerson(cartItem.getBuyerContactPerson());
			orderItem.setBuyerEmail(cartItem.getBuyerEmail());
			orderItem.setBuyerName(cartItem.getBuyerName());
			orderItem.setBuyerPhone(cartItem.getBuyerPhone());
			orderItem.setBuyer(getBuyer(orderItem));
			orderItem.setResellerOrder(order);

			orderItem.setProduct(cartItem.getProduct());
			orderItem.setPriceType(cartItem.getOrderType());
			orderItem.setAmountWithoutTax(cartItem.getAmountWithoutTax());
			orderItem.setAmountWithTax(cartItem.getAmountWithTax());
			orderItem.setDiscount(cartItem.getDiscount());
			BigDecimal unitPrice = cartItem.getBasicPrice();
			if (Boolean.TRUE.equals(cartItem.getProduct().getTaxIncludedInPrice())) {
				unitPrice = unitPrice.divide(OptitronicsConstants.TAX_PLUS_ONE, 2, RoundingMode.HALF_UP);
			}
			orderItem.setUnitPrice(unitPrice);

			if (OptitronicsConstants.ORDER_TYPE_RENEWAL.equals(cartItem.getOrderType())) {
				String fileName = reportService.getOrderItemFileName(orderItem) + ".pdf";
				File outFile = new File(fileName);
				FileCopyUtils.copy(cartItem.getFileContent(), outFile);
			}

			orderItem.persist();
		}

		this.items.clear();
		this.totalAmoutWithoutTax = BigDecimal.ZERO;
		this.totalAmoutWithTax = BigDecimal.ZERO;

		return order;
	}

	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
	private Client getBuyer(OrderItem orderItem) {
		Client buyer = Client.findClientByNameAndEmail(orderItem.getBuyerName(), orderItem.getBuyerEmail());
		if (buyer == null) {
			buyer = new Client();
			buyer.setName(orderItem.getBuyerName());
			buyer.setEmail(orderItem.getBuyerEmail());
			buyer.setAddress(orderItem.getBuyerAddress());
			buyer.setCity(orderItem.getBuyerCity());
			buyer.setContactPerson(orderItem.getBuyerContactPerson());
			buyer.setPhone(orderItem.getBuyerPhone());
			buyer.persist();
		}

		return buyer;
	}

	/**
	 * Adds item to shopping cart
	 * 
	 * @param item
	 */
	public void addItemToShoppingCart(ShoppingCartItem item) {
		logger.info(OptitronicsUtils.getLoggerPrefix() + ": " + "Adding shopping cart item for productId: "
				+ item.getProductId());

		if (getItemIndexById(item.getItemId()) == -1) {
			this.items.add(item);
		}
		item.loadFileContent();
		recalculateShoppingCart();
	}

	private int getItemIndexById(Long itemId) {
		ShoppingCartItem tempItem = new ShoppingCartItem(itemId);

		return this.items.indexOf(tempItem);
	}

	public ShoppingCartItem getItemById(Long itemId) {
		int itemIndex = getItemIndexById(itemId);
		return this.items.get(itemIndex);
	}

	/**
	 * Removes item from shopping cart
	 * 
	 * @param itemId
	 */
	public void removeShoppingCartItem(Long itemId) {

		int itemIndex = getItemIndexById(itemId);
		this.items.remove(itemIndex);
		recalculateShoppingCart();
	}

	private void recalculateShoppingCart() {

		this.totalAmoutWithoutTax = BigDecimal.ZERO;
		this.totalAmoutWithTax = BigDecimal.ZERO;

		for (ShoppingCartItem item : items) {

			recalculateShoppingCartItem(item);

			this.totalAmoutWithoutTax = this.totalAmoutWithoutTax.add(item.getAmountWithoutTax());
			this.totalAmoutWithTax = this.totalAmoutWithTax.add(item.getAmountWithTax());
		}
	}

	public void recalculateShoppingCartItem(ShoppingCartItem item) {

		BigDecimal quantity = BigDecimal.valueOf(item.getQuantity());
		BigDecimal price = BigDecimal.ZERO;
		BigDecimal priceWithTax = BigDecimal.ZERO;
		BigDecimal priceWithoutTax = BigDecimal.ZERO;

		if (OptitronicsConstants.ORDER_TYPE_NEW.equals(item.getOrderType())) {
			price = item.getProduct().getPriceNew();
		} else if (OptitronicsConstants.ORDER_TYPE_RENEWAL.equals(item.getOrderType())) {
			price = item.getProduct().getPriceReneval();
		} else if (OptitronicsConstants.ORDER_TYPE_ACADEMIC.equals(item.getOrderType())) {
			price = item.getProduct().getPriceAcademic();
		} else if (OptitronicsConstants.ORDER_TYPE_STUDENT.equals(item.getOrderType())) {
			price = item.getProduct().getPriceStudent();
		} else if (OptitronicsConstants.ORDER_TYPE_PENSIONER.equals(item.getOrderType())) {
			price = item.getProduct().getPricePensioner();
		} else if (OptitronicsConstants.ORDER_TYPE_NUNS.equals(item.getOrderType())) {
			price = item.getProduct().getPriceNuns();
		} else if (OptitronicsConstants.ORDER_GRAPHIC_DESIGNER.equals(item.getOrderType())) {
			price = item.getProduct().getPriceGraphicDesigner();
		}

		item.setBasicPrice(price);
		BigDecimal discount = item.getProduct().getDiscount() != null ? item.getProduct().getDiscount()
				: BigDecimal.ZERO;
		BigDecimal priceWithDiscount = BigDecimal.valueOf(100.00).subtract(discount).divide(BigDecimal.valueOf(100.00))
				.multiply(price);
		item.setPriceWithDiscount(priceWithDiscount.setScale(2, BigDecimal.ROUND_HALF_UP));

		BigDecimal totalAmount = priceWithDiscount.multiply(quantity).setScale(2, BigDecimal.ROUND_HALF_UP);

		if (Boolean.TRUE.equals(item.getProduct().getTaxIncludedInPrice())) {
			priceWithTax = totalAmount;
			priceWithoutTax = totalAmount.divide(OptitronicsConstants.TAX_PLUS_ONE, 2, RoundingMode.HALF_UP);
		} else {
			priceWithoutTax = totalAmount;
			priceWithTax = totalAmount.multiply(OptitronicsConstants.TAX_PLUS_ONE)
					.setScale(2, BigDecimal.ROUND_HALF_UP);
		}

		item.setAmountWithoutTax(priceWithoutTax);
		item.setAmountWithTax(priceWithTax);
	}

	public Long getCartId() {
		return cartId;
	}

	public void setCartId(Long cartId) {
		this.cartId = cartId;
	}

	public List<ShoppingCartItem> getItems() {
		return items;
	}

	public void setItems(List<ShoppingCartItem> items) {
		this.items = items;
	}

	public BigDecimal getTotalAmoutWithoutTax() {
		return totalAmoutWithoutTax;
	}

	public void setTotalAmoutWithoutTax(BigDecimal totalAmoutWithoutTax) {
		this.totalAmoutWithoutTax = totalAmoutWithoutTax;
	}

	public BigDecimal getTotalAmoutWithTax() {
		return totalAmoutWithTax;
	}

	public void setTotalAmoutWithTax(BigDecimal totalAmoutWithTax) {
		this.totalAmoutWithTax = totalAmoutWithTax;
	}

}
